﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2024 LiLong                                                   **
**  This file is part of GnssLogs.                                              **
**                                                                              **
**  GnssLogs is free software: you can redistribute it and/or modify            **
**  it under the terms of the GNU Lesser General Public License as published by **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  GnssLogs is distributed in the hope that it will be useful,                 **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU Lesser General Public License for more details.                         **
**                                                                              **
**  You should have received a copy of the GNU Lesser General Public License    **
**  along with GnssLogs.  If not, see <https://www.gnu.org/licenses/>.          **
**********************************************************************************/
#pragma once

#include "../GGA.h"
#include "Commons.h"

namespace GnssLogs
{
namespace GGADecoder
{
using namespace tao::pegtl;
using LogHeader = NMEA_TALKER_IDENTIFIER("GGA");

// clang-format off
struct Lat : Floating {};
struct LatDir : one<'N', 'S'> {};
struct Lon : Floating {};
struct LonDir : one<'E', 'W'> {};
struct Quality : digit {};
struct Sats : seq<digit, digit> {};
struct HDOP : Floating {};
struct Alt : Floating {};
struct AUnits : one<'M'> {};
struct Undulation : Floating {};
struct UUnits : one<'M'> {};
struct Age : seq<digit, digit> {};
struct StnID : rep<4, alnum> {};
// clang-format on

using Expression = seq<LogHeader,
                       Split,
                       Utc,
                       Split,
                       Lat,
                       Split,
                       LatDir,
                       Split,
                       Lon,
                       Split,
                       LonDir,
                       Split,
                       Quality,
                       Split,
                       Sats,
                       Split,
                       HDOP,
                       Split,
                       Alt,
                       Split,
                       AUnits,
                       Split,
                       Undulation,
                       Split,
                       UUnits,
                       Split,
                       opt<Age>,
                       Split,
                       opt<StnID>,
                       one<'*'>,
                       CheckSum,
                       Tail>;

template<typename T>
struct Action
{
};

template<>
struct Action<LogHeader>
{
    template<typename ParseInput>
    static bool apply(ParseInput& in, GGA::Data& data)
    {
        data.header = std::string(in.begin(), in.size());
        return true;
    }
};

template<>
struct Action<Utc> : UTCHelperNoSetValue
{
};

template<>
struct Action<Lat> : DoubleHelper<Action<Lat>>
{
    static void setValue(GGA::Data& data, double value) { data.lat = value; }
};

template<>
struct Action<LatDir>
{
    template<typename ParseInput>
    static bool apply(ParseInput& in, GGA::Data& data)
    {
        data.latdir = std::string(in.begin(), in.size());
        return true;
    }
};

template<>
struct Action<Lon> : DoubleHelper<Action<Lon>>
{
    static void setValue(GGA::Data& data, double value) { data.lon = value; }
};

template<>
struct Action<LonDir>
{
    template<typename ParseInput>
    static bool apply(ParseInput& in, GGA::Data& data)
    {
        data.londir = std::string(in.begin(), in.size());
        return true;
    }
};

template<>
struct Action<Quality> : IntHelper<Action<Quality>, uint8_t>
{
    static void setValue(GGA::Data& data, uint8_t value) { data.quality = value; }
};

template<>
struct Action<Sats> : IntHelper<Action<Sats>, uint8_t>
{
    static void setValue(GGA::Data& data, uint8_t value) { data.sats = value; }
};

template<>
struct Action<HDOP> : DoubleHelper<Action<HDOP>>
{
    static void setValue(GGA::Data& data, double value) { data.hdop = value; }
};

template<>
struct Action<Alt> : DoubleHelper<Action<Alt>>
{
    static void setValue(GGA::Data& data, double value) { data.alt = value; }
};

template<>
struct Action<AUnits>
{
    template<typename ParseInput>
    static bool apply(ParseInput& in, GGA::Data& data)
    {
        data.altitudeUnits = std::string(in.begin(), in.size());
        return true;
    }
};

template<>
struct Action<Undulation> : DoubleHelper<Action<Undulation>>
{
    static void setValue(GGA::Data& data, double value) { data.undulation = value; }
};

template<>
struct Action<UUnits>
{
    template<typename ParseInput>
    static bool apply(ParseInput& in, GGA::Data& data)
    {
        data.undulationUnits = std::string(in.begin(), in.size());
        return true;
    }
};

template<>
struct Action<Age> : IntHelper<Action<Age>, uint8_t>
{
    static void setValue(GGA::Data& data, uint8_t value) { data.age = value; }
};

template<>
struct Action<StnID>
{
    template<typename ParseInput>
    static bool apply(ParseInput& in, GGA::Data& data)
    {
        data.stnID = std::string(in.begin(), in.size());
        return true;
    }
};

template<>
struct Action<CheckSum> : CheckSumHelper<Action<CheckSum>>
{
    static void setValue(GGA::Data& data, uint8_t value) { data.checkSum = value; }
};
} // namespace GGADecoder
} // namespace GnssLogs